#include "TextUtils.h"

// 单词转小写
void to_lower_case(string& s) {
    // std::transform(起始迭代器, 结束迭代器, 结果起始迭代器, 操作函数)
    
    // 在这个例子中，输入范围和输出范围相同，因此是对字符串进行原地修改。
    std::transform(s.begin(), s.end(), s.begin(), 
        [](unsigned char c){
            // 必须使用 static_cast<unsigned char> 来处理 cctype 函数，
            // 以避免字符被提升为负的 int 值，确保正确性。
            return std::tolower(c);
        }
    );
}

// 原地去除字符串的首尾空白
void trim_inplace(string &s) {
    // 1. 去除尾部空白：从后向前查找第一个非空白字符
    s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
        return !std::isspace(ch);
    }).base(), s.end());

    // 2. 去除首部空白：从前向后查找第一个非空白字符
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
        return !std::isspace(ch);
    }));
}

// 去除一个string对象中的非字母
void remove_string_digits(string& s) {
    auto new_end = std::remove_if(s.begin(), s.end(), 
        [](unsigned char c) {
            // 条件：返回 true 表示该字符应该被移除（即它是非字母）
            return !std::isalpha(c); 
        }
    );
    s.erase(new_end, s.end());
}

// 对数据进行清洗, 去除数字和标点符号, 将字母统一转换成小写
void clean_and_normalize(vector<string>& result) {
    for(auto &word : result){
        // 去除一个string对象中的非字母
        remove_string_digits(word);
        // 转小写
        to_lower_case(word);
    }
}

// 判断是否为停用词
bool isStopWord(const string& word, const set<string>& stopWords) {
    if (stopWords.find(word)!=stopWords.end()) { 
        return true;
    }
    return false;
}

//判断是否为中文字符
bool isChineseChar(char32_t cp){
    // 基本汉字 + 扩展A + 扩展B
    return ( (cp >= 0x4E00 && cp <= 0x9FFF) ||   
             (cp >= 0x3400 && cp <= 0x4DBF) ||   
             (cp >= 0x20000 && cp <= 0x2A6DF) ); 
}

// 判断是否可以为keyWord
bool isValidKeyWord(string &word, const set<string>& StopWord){
    //对jieba分词结果做修剪处理
    trim_inplace(word);

    //判断是否为空
    if(word.empty()){
        return false;
    }

    //判断是否为停用词
    if(isStopWord(word,StopWord)){
        return false;
    }

    //判断是否为中文字符
    auto it = word.begin();
    while(it != word.end()){
        //UTF编码最大为4个字节
        char32_t cp = utf8::next(it,word.end());
        if(isChineseChar(cp)){
            continue;
        }else{
            return false;
        }
    }
    return true;
}

// 得到停用词集合
void generate_stopwords(const string& dir, set<string>& stopwords){
    ifstream ifs(dir);
    string stopWord; // 变量名使用单数形式更清晰

    // 检查文件是否成功打开
    if (!ifs.is_open()){
        cerr << "Error: Failed to open stopword file." << endl;
        return;
    }else{
        // 逐行读取停用词并插入集合
        while (std::getline(ifs, stopWord)){
            // 去除首尾空白
            trim_inplace(stopWord);
            stopwords.insert(stopWord);
        }

        // 关闭文件流
        ifs.close();
    }
}

// 使用正则表达式提取标签内容
// 匹配 <tag>CONTENT</tag>
string extract_tag_content(const string& xml_content, const string& tag_name) {
    // 构建正则表达式: <tag_name>(.+?)</tag_name>
    // (.+?) 匹配内容，? 确保非贪婪匹配
    string pattern = "<" + tag_name + ">(.*?)</" + tag_name + ">";
    regex r(pattern);
    smatch m;

    if (std::regex_search(xml_content, m, r) && m.size() > 1) {
        // m[1] 包含第一个捕获组的内容 (即标签内的内容)
        return m[1].str();
    }
    return ""; // 如果未找到，返回空字符串
}

// 从page_library.dat读取指定位置的内容
Document get_document(ifstream& ifs, streamoff start_pos, streamoff length) {
    Document doc;
    
    // 检查输入流是否正常
    if (!ifs.is_open() || ifs.fail()) {
        cerr << "Error: Input file stream is not open or in a bad state." << endl;
        return doc; // 返回空 Document
    }

    // 清除流的错误状态
    ifs.clear(); 
    
    // 定位到指定起始位置
    ifs.seekg(start_pos, std::ios::beg);
    
    // 检查定位是否成功
    if (ifs.fail()) {
        cerr << "Error: Failed to seek to start_pos: " << start_pos << endl;
        return doc;
    }
    
    // 使用 std::string 来存储读取的内容
    string xml_buffer;
    xml_buffer.resize(length);
    
    // 读取指定长度的字节到缓冲区
    ifs.read(&xml_buffer[0], length);
    
    // 检查读取是否成功，且实际读取的字节数是否等于预期长度
    if (ifs.fail() || ifs.gcount() != length) {
        cerr << "Warning: Read failed or only read " << ifs.gcount() 
                  << " bytes (expected " << length << ")." << endl;
        // 注意：即使读取失败，gcount() 仍可能返回部分读取的数据，我们继续尝试解析这部分数据
    }
    
    // 解析 <id>
    string id_str = extract_tag_content(xml_buffer, "id");
    try {
        doc.id = std::stoi(id_str);
    } catch (const std::exception& e) {
        doc.id = -1; // 解析失败则设为默认值
        std::cerr << "Warning: Failed to parse ID: " << id_str << endl;
    }

    // 解析 <link>
    doc.link = extract_tag_content(xml_buffer, "link");

    // 解析 <title>
    doc.title = extract_tag_content(xml_buffer, "title");
    
    // 解析 <content>
    doc.content = extract_tag_content(xml_buffer, "content");
    
    // 清理 content 开头的空白符
    size_t first_char = doc.content.find_first_not_of(" \t\n\r");
    if (first_char != std::string::npos) {
        doc.content = doc.content.substr(first_char);
    }
    
    return doc;
}

// 过滤非中文或包含非中文的词
bool is_all_non_chinese(const string& word) {
    if (word.empty()) return true;
    
    // 检查是否全由数字和/或英文字母组成
    bool has_chinese = false;
    // 使用 utf8::next 安全遍历
    const char* it = word.c_str();
    const char* end = word.c_str() + word.size();
    
    while (it != end) {
        auto start = it;
        utf8::next(it, end);
        std::string alpha = std::string(start, it);
        
        // 粗略判断：如果字节长度大于1，通常是中文或多字节符号
        // 精确判断：检查字符的 unicode 范围是否落在 CJK 区域
        if (alpha.size() > 1 || (alpha.size() == 1 && (unsigned char)alpha[0] > 127)) {
            // 这里可以添加更精确的 CJK 范围检查
            has_chinese = true;
            break; // 只要有一个中文/多字节字符就认为不是纯英文/数字
        }
    }
    
    // 如果一个汉字都没有，且包含数字/字母，则过滤掉。
    // 更简单粗暴的判断：如果一个中文字符都没有，且长度较短（可能是符号），则视为非中文词汇。
    return !has_chinese; 
}